package ee.ut.aa.neuraltic.logic;

import org.apache.log4j.Logger;

import ee.ut.aa.neuraltic.model.Board;

public class TicEvaluator implements BoardEvaluator {

	private static Logger log = Logger.getLogger( TicEvaluator.class );

	private static TicEvaluator eval;

	private TicEvaluator() {

	}

	public static TicEvaluator getInstance() {

		if( eval == null ) {
			eval = new TicEvaluator();
		}

		return eval;
	}

	@Override
	public int findWinner( Board board ) {

		int[] fields = board.getFields();

		for( int i = 0; i < board.getSize(); i++ ) {

			if( isWinningSpot( fields, i ) )
				return fields[i];
		}

		return 0;
	}

	@Override
	public boolean isGameOver( Board board ) {

		int[] fields = board.getFields();

		log.debug( "Evaluating board." );

		if( board.getLegalMoves().size() == 0 )
			return true;

		// Optimized for tic tac toe = ~6% speed optimization
		// left to right
		if( fields[0] == 1 && fields[1] == 1 && fields[2] == 1 ) return true;
		if( fields[3] == 1 && fields[4] == 1 && fields[5] == 1 ) return true;
		if( fields[6] == 1 && fields[7] == 1 && fields[8] == 1 ) return true;
		
		if( fields[0] == -1 && fields[1] == -1 && fields[2] == -1 ) return true;
		if( fields[3] == -1 && fields[4] == -1 && fields[5] == -1 ) return true;
		if( fields[6] == -1 && fields[7] == -1 && fields[8] == -1 ) return true;
		
		// top to bottom
		if( fields[0] == 1 && fields[3] == 1 && fields[6] == 1 ) return true;
		if( fields[1] == 1 && fields[4] == 1 && fields[7] == 1 ) return true;
		if( fields[2] == 1 && fields[5] == 1 && fields[8] == 1 ) return true;

		if( fields[0] == -1 && fields[3] == -1 && fields[6] == -1 ) return true;
		if( fields[1] == -1 && fields[4] == -1 && fields[7] == -1 ) return true;
		if( fields[2] == -1 && fields[5] == -1 && fields[8] == -1 ) return true;
		
		// diagonals
		if( fields[0] == 1 && fields[4] == 1 && fields[8] == 1 ) return true;
		if( fields[2] == 1 && fields[4] == 1 && fields[6] == 1 ) return true;

		if( fields[0] == -1 && fields[4] == -1 && fields[8] == -1 ) return true;
		if( fields[2] == -1 && fields[4] == -1 && fields[6] == -1 ) return true;
		
		
		
//		for( int i = 0; i < fields.length; i++ ) {
//
//			result = isWinningSpot( fields, i );
//
//			if( result )
//				return true;
//		}

		log.debug( "Game is not over.. returning FALSE" );

		return false;
	}

	private boolean isWinningSpot( int[] fields, int index ) {

		int size = 9;
		int width = 3;

		int player = fields[index];

		if( player == 0 )
			return false;

		log.debug( "Checking if spot index=" + index + " is winning spot for player=" + player );

		boolean leToRi = leftToRight( fields, index, player, size, width );

		boolean toToBo = topToBottom( fields, index, player, size, width );

		boolean diagon = diagonal( fields, index, player, size, width );

		boolean diaRev = diagReverse( fields, index, player, size, width );

		if( leToRi || toToBo || diagon || diaRev )
			return true;

		log.debug( "Did not find any winning combination for player=" + player );

		return false;
	}

	private boolean leftToRight( int[] fields, int index, int player, int size, int width ) {

		int maxLength = 0;

		// Check left-to-right
		for( int i = index; i < index + 3 && i < size; i++ ) {

			if( fields[i] == player )
				maxLength++;
		}

		log.debug( "Max length on left-to-right= " + maxLength );

		// Check if was winning move
		// Check warping to next row as well
		if( maxLength >= 3 && width - ( index % width ) >= 3 )
			return true;
		else
			return false;
	}

	private boolean topToBottom( int[] fields, int index, int player, int size, int width ) {

		int maxLength = 0;

		for( int i = index; i < index + 3 * width && i < size; i = i + width ) {
			if( fields[i] == player )
				maxLength++;
		}

		log.debug( "Max length on top-to-bottom= " + maxLength );

		// Check if was winning move
		// No warping to next row check needed here
		if( maxLength >= 3 )
			return true;

		return false;
	}

	private boolean diagonal( int[] fields, int index, int player, int size, int width ) {

		int maxLength = 0;

		for( int i = index; i < index + 3 * width + width && i < size; i = i + 1 + width ) {
			if( fields[i] == player )
				maxLength++;
			else
				break;
		}

		log.debug( "Max length on diagonal= " + maxLength );

		// Check if was winning move
		// Check warping to next row as well - same check as in left-to-right
		if( maxLength >= 3 && width - ( index % width ) >= 3 )
			return true;

		return false;
	}

	private boolean diagReverse( int[] fields, int index, int player, int size, int width ) {

		int maxLength = 0;

		for( int i = index; i < index + 3 * width + width && i < size; i = i - 1 + width ) {
			if( fields[i] == player )
				maxLength++;
			else
				break;
		}

		log.debug( "Max length on reverse diagonal= " + maxLength );

		// Check if was winning move
		// Check warping to next row as well - almost same check as in
		// left-to-right
		if( maxLength >= 3 && index % width >= 3 - 1 )
			return true;

		return false;
	}
}
